#include "app_buff.h"

/**
 * @brief 切换缓冲区
 * @param buffer 缓冲区指针
 * @return int 0 成功 -1 失败
 */
static int app_buff_swap(buffer_t *buffer)
{
    log_debug("切换缓冲区");
    pthread_mutex_lock(&buffer->swap_lock);
    buffer->write_index = buffer->write_index == 0 ? 1 : 0;
    buffer->red_index = buffer->red_index == 0 ? 1 : 0;
    pthread_mutex_unlock(&buffer->swap_lock);
    return 0;
}

/**
 * @brief 缓冲区的初始化
 * @param total_size 缓冲区总大小 因为是双缓冲结构 所以总大小为2倍
 * @return buffer_t* 缓冲区指针
 * @note 创建两个缓冲区，放入主缓冲区结构体，然后返回
 */
buffer_t *app_buff_init(int total_size)
{
    // 主缓冲区结构体
    buffer_t *buff = (buffer_t *)malloc(sizeof(buffer_t));
    if (buff == NULL)
    {
        log_error("app_buff_init error");
        return NULL;
    }
    // 创建两个子缓冲区
    for (int i = 0; i < 2; i++)
    {
        buff->sub_buffer[i] = (sub_buffer_t *)malloc(sizeof(sub_buffer_t));
        buff->sub_buffer[i]->data = (char *)malloc(total_size);
        buff->sub_buffer[i]->len = 0;
        buff->sub_buffer[i]->total_size = total_size;
    }
    // 确定读写职责
    buff->red_index = 0;
    buff->write_index = 1;

    // 初始化读锁
    pthread_mutex_init(&buff->read_lock, NULL);
    // 初始化交换锁
    pthread_mutex_init(&buff->swap_lock, NULL);
    return buff;
}

/**
 * @brief 向缓冲区写入数据
 * @param buff 缓冲区指针
 * @param data 写入的数据指针
 * @param len 写入的数据长度
 * @return int 写入成功返回写入长度，失败返回-1
 * @note 判断是否可以写入数据 比较写入长度和剩余空间 \
 * 如果可以写入数据，则向尾部拷贝数据，更新当前缓冲区长度
 */
int app_buff_write(buffer_t *buff, char *data, u_int8_t len)
{
    // 加交换锁
    pthread_mutex_lock(&buff->swap_lock);
    // 取得当前负责写入的缓冲区
    sub_buffer_t *write_sub_buffer = buff->sub_buffer[buff->write_index];
    // 判断是否可以写入数据 比较写入长度和剩余空间
    int remain_len = write_sub_buffer->total_size - write_sub_buffer->len;
    if (remain_len <= len)
    {
        log_error("当前缓冲区剩余空间不足，请检查数据长度");
        return -1;
    }
    // 如果可以写入数据，则向尾部拷贝数据
    write_sub_buffer->data[write_sub_buffer->len] = len;  // 帧头长度
    memcpy(write_sub_buffer->data + write_sub_buffer->len + 1, data, (size_t)len); // 数据

    // 更新当前缓冲区长度
    write_sub_buffer->len += (len + 1);

    // 解锁
    pthread_mutex_unlock(&buff->swap_lock);

    return 0;
}

/**
 * @brief 从缓冲区读取数据
 * @param buffer 缓冲区指针
 * @param r_buff 读取的数据指针 （已经开辟空间）
 * @param buff_len 读取的数据长度
 * @return int 实际读取数据的长度
 * @note 判断容器是否够放一条数据，比较容器长度和数据长度 \
 * 判断当前读缓冲是否为空 要做切换。从缓冲区开头位置读取一条数据 \
 * 要从缓冲区删除数据，更新当前缓冲区长度。如果缓冲区为空，则切换缓冲区。
 */
int app_buff_read(buffer_t *buffer, char *r_buff, int buff_len)
{
    // 加锁 因为线程会改变 buffer->read_index
    pthread_mutex_lock(&buffer->read_lock);
    // 取得当前负责读取的缓冲区
    sub_buffer_t *read_sub_buffer = buffer->sub_buffer[buffer->red_index];
    // 判断当前读缓冲是否为空 要做切换
    if (read_sub_buffer->len == 0)
    {
        // 切换缓冲区
        app_buff_swap(buffer);
        // 有疑问   晚上打断点看========================================
        // 切换分区后重新获取缓冲区 ！！！！！！！！！！！！！！！！！！！！！！！！！！
        read_sub_buffer = buffer->sub_buffer[buffer->red_index];
        // ======================================
        // 再进行一次判断
        if (read_sub_buffer->len == 0)
        {
            log_error("缓冲区为空，请检查数据长度");
            return 0;
        }
    }
    // 判断容器是否够放一条数据，比较容器长度和数据长度
    int data_len = read_sub_buffer->data[0];  // 要提取数据的帧头长度
    if (buff_len < data_len)
    {
        log_error("容器空间不足");
        return -1;
    }
    // 从缓冲区开头位置读取一条数据
    memcpy(r_buff, read_sub_buffer->data + 1, data_len);
    // 从缓冲区删除数据
    memmove(read_sub_buffer->data, read_sub_buffer->data + data_len + 1, read_sub_buffer->len - data_len - 1);
    // 更新当前缓冲区长度
    read_sub_buffer->len -= (data_len + 1);
    // 如果缓冲区为空，则切换缓冲区 (做不做都行)
    if (read_sub_buffer->len == 0)
    {
        // 切换缓冲区
        app_buff_swap(buffer);
    }
    pthread_mutex_unlock(&buffer->read_lock);

    return data_len;
}

/**
 * @brief 销毁缓冲区
 * @param buffer 缓冲区指针
 * @note 先释放子缓冲区指针，再释放主缓冲区指针
 */
void app_buff_destroy(buffer_t *buffer)
{
    for (int i = 0; i < 2; i++)
    {
        free(buffer->sub_buffer[i]->data);
        free(buffer->sub_buffer[i]);
    }
    // 释放锁
    pthread_mutex_destroy(&buffer->read_lock);
    pthread_mutex_destroy(&buffer->swap_lock);
    free(buffer);
}